/* Copyright 2009
 *
 * This program and the accompanying materials
 * are made available under the terms of the
 * Eclipse Public License v1.0 which accompanies
 * this distribution, and is available at
 *
 * 		http://www.eclipse.org/legal/epl-v10.html
 *
 * Unless required by applicable law or agreed to in
 * writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
 * OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * Contributors:
 * 	   IBM Corporation - initial API and implementation for JDT/DLTK
 *     Sean W. Quinn - initial adoption for use with PHP from various sources.
 */
package org.eclipse.php.internal.core;

import java.util.Map;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.IParent;
import org.eclipse.dltk.core.ISourceManipulation;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.core.WorkingCopyOwner;
import org.eclipse.dltk.internal.core.ModelElementInfo;
import org.eclipse.dltk.internal.core.Openable;
import org.eclipse.dltk.internal.core.OpenableElementInfo;
import org.eclipse.dltk.internal.core.util.MementoTokenizer;
import org.eclipse.dltk.internal.core.util.Util;
import org.eclipse.dltk.utils.CorePrinter;
import org.eclipse.php.core.INamespaceFragment;
import org.eclipse.php.core.INamespaceFragmentRoot;
import org.eclipse.php.core.IPHPElement;
import org.eclipse.php.core.IPHPModel;
import org.eclipse.php.core.IPHPProject;
import org.eclipse.php.core.IProgramUnit;
import org.eclipse.php.core.IScriptFile;
import org.eclipse.php.core.PHPModelException;
import org.eclipse.php.internal.compiler.util.SuffixConstants;

/**
 * @see INamespaceFragment
 * @see org.eclipse.jdt.internal.core.PackageFragment
 */
@SuppressWarnings("restriction")
public class NamespaceFragment extends Openable implements INamespaceFragment, SuffixConstants {
	/**
	 * Constant empty list of class files
	 */
	protected static final IScriptFile[] NO_SCRIPTFILES = new IScriptFile[] {};
	/**
	 * Constant empty list of compilation units
	 */
	protected static final IProgramUnit[] NO_PROGRAM_UNITS = new IProgramUnit[] {};

	public String[] names;

	protected NamespaceFragment(NamespaceFragmentRoot root, String[] names) {
		super(root);
		this.names = names;
	}

	/**
	 * @throws ModelException
	 * @see Openable
	 */
	@Override
	protected boolean buildStructure(OpenableElementInfo info, IProgressMonitor pm, Map newElements,
			IResource underlyingResource) throws PHPModelException, ModelException {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#buildStructure() not yet implemented!");
	}

	/**
	 * Returns true if this fragment contains at least one java resource.
	 * Returns false otherwise.
	 * @throws ModelException
	 */
	public boolean containsPHPResources() throws ModelException {
		return ((NamespaceFragmentInfo) getElementInfo()).containsPHPResources();
	}

	/**
	 * @see ISourceManipulation
	 */
	public void copy(IModelElement container, IModelElement sibling, String rename, boolean force,
			IProgressMonitor monitor) {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#copy() not yet implemented!");
	}

	/**
	 * @see INamespaceFragment
	 */
	public IProgramUnit createProgramUnit(String cuName, String contents, boolean force, IProgressMonitor monitor)
			throws PHPModelException {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#createProgramUnit() not yet implemented!");
	}

	/**
	 * @see PHPElement
	 */
	@Override
	protected Object createElementInfo() {
		return new NamespaceFragmentInfo();
	}

	/**
	 * @see ISourceManipulation
	 */
	public void delete(boolean force, IProgressMonitor monitor) throws PHPModelException {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#delete() not yet implemented!");
	}

	@Override
	public boolean equals(Object o) {
		if (this == o)
			return true;
		if (!(o instanceof NamespaceFragment))
			return false;

		NamespaceFragment other = (NamespaceFragment) o;
		return Util.equalArraysOrNull(this.names, other.names) && this.parent.equals(other.parent);
	}

	@Override
	public boolean exists() {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#exists() not yet implemented!");
	}

	/**
	 * @see INamespaceFragment#getScriptFile(String)
	 * @exception IllegalArgumentException if the name does not end with ".php"
	 */
	public IScriptFile getScriptFile(String scriptFileName) {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#getScriptFile() not yet implemented!");
	}

	/**
	 * Returns a the collection of class files in this - a folder package
	 * fragment which has a root that has its kind set to
	 * <code>INamespaceFragmentRoot.K_Source</code> does not recognize class
	 * files.
	 *
	 * @see INamespaceFragment#getClassFiles()
	 */
	public IScriptFile[] getScriptFiles() throws PHPModelException {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#getScriptFiles() not yet implemented!");
	}

	/**
	 * @see INamespaceFragment#getProgramExt(String)
	 * @exception IllegalArgumentException if the name does not end with ".java"
	 */
	public IProgramUnit getProgramUnit(final String unitName) {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#getProgramUnit() not yet implemented!");
	}

	/**
	 * @see INamespaceFragment#getProgramUnits()
	 */
	public IProgramUnit[] getProgramUnits() throws PHPModelException {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#getProgramUnits() not yet implemented!");
	}

	/**
	 * @see INamespaceFragment#getProgramUnits(WorkingCopyOwner)
	 */
	public IProgramUnit[] getProgramUnits(WorkingCopyOwner owner) {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#getProgramUnits() not yet implemented!");
	}

	@Override
	public String getElementName() {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#getElementName() not yet implemented!");
	}

	/**
	 * @see IPHPElement
	 */
	public int getElementType() {
		return NAMESPACE_FRAGMENT;
	}

	/*
	 * @see PHPElement
	 */
	@Override
	public IPHPElement getHandleFromMemento(String token, MementoTokenizer memento, WorkingCopyOwner owner) {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#getHandleFromMemento() not yet implemented!");
	}

	/**
	 * @see PHPElement#getHandleMementoDelimiter()
	 */
	@Override
	protected char getHandleMementoDelimiter() {
		return PHPElement.JEM_NAMESPACEFRAGMENT;
	}

	/**
	 * @see INamespaceFragment#getKind()
	 */
	public int getKind() throws ModelException {
		return ((INamespaceFragmentRoot) getParent()).getKind();
	}

	/**
	 * Returns an array of non-java resources contained in the receiver.
	 */
	public Object[] getNonPHPResources() throws ModelException {
		if (isDefaultNamespace()) {
			// We don't want to show non java resources of the default package (see PR #1G58NB8)
			return ModelElementInfo.NO_NON_SCRIPT_RESOURCES;
		}
		else {
			return ((NamespaceFragmentInfo) getElementInfo()).getNonPHPResources(getResource(),
					getNamespaceFragmentRoot());
		}
	}

	/**
	 * Find enclosing package fragment root if any
	 */
	public NamespaceFragmentRoot getNamespaceFragmentRoot() {
		return (NamespaceFragmentRoot) getAncestor(IPHPElement.NAMESPACE_FRAGMENT_ROOT);
	}

	/**
	 */
	public IPath getPath() {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#getPath() not yet implemented!");
	}

	/**
	 * @see PHPElement#getResource()
	 */
	public IResource getResource(NamespaceFragmentRoot root) {
		int length = this.names.length;
		if (length == 0) {
			return root.resource(root);
		}
		else {
			IPath path = new Path(this.names[0]);
			for (int i = 1; i < length; i++)
				path = path.append(this.names[i]);
			return ((IContainer) root.resource(root)).getFolder(path);
		}
	}

	/**
	 * @see IPHPElement#getUnderlyingResource()
	 */
	@Override
	public IResource getUnderlyingResource() throws ModelException {
		IResource rootResource = this.parent.getUnderlyingResource();
		if (rootResource == null) {
			//jar package fragment root that has no associated resource
			return null;
		}
		// the underlying resource may be a folder or a project (in the case that the project folder
		// is atually the package fragment root)
		if (rootResource.getType() == IResource.FOLDER || rootResource.getType() == IResource.PROJECT) {
			IContainer folder = (IContainer) rootResource;
			String[] segs = this.names;
			for (int i = 0; i < segs.length; ++i) {
				IResource child = folder.findMember(segs[i]);
				if (child == null || child.getType() != IResource.FOLDER) {
					throw newNotPresentException();
				}
				folder = (IFolder) child;
			}
			return folder;
		}
		else {
			return rootResource;
		}
	}

	@Override
	public int hashCode() {
		int hash = this.parent.hashCode();
		for (int i = 0, length = this.names.length; i < length; i++)
			hash = Util.combineHashCodes(this.names[i].hashCode(), hash);
		return hash;
	}

	/**
	 * @see IParent
	 */
	@Override
	public boolean hasChildren() throws ModelException {
		return getChildren().length > 0;
	}

	/**
	 * @throws ModelException
	 * @see INamespaceFragment#hasSubpackages()
	 */
	public boolean hasSubnamespaces() throws ModelException {
		IPHPElement[] packages = (IPHPElement[]) ((INamespaceFragmentRoot) getParent()).getChildren();
		int namesLength = this.names.length;
		nextPackage: for (int i = 0, length = packages.length; i < length; i++) {
			String[] otherNames = ((NamespaceFragment) packages[i]).names;
			if (otherNames.length <= namesLength)
				continue nextPackage;
			for (int j = 0; j < namesLength; j++)
				if (!this.names[j].equals(otherNames[j]))
					continue nextPackage;
			return true;
		}
		return false;
	}

	/**
	 * @see INamespaceFragment#isDefaultNamespace()
	 */
	public boolean isDefaultNamespace() {
		return this.names.length == 0;
	}

	private boolean isValidPackageName() {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#isValidPackageName() not yet implemented!");
	}

	/**
	 * @see ISourceManipulation#move(IModelElement, IModelElement, String,
	 *      boolean, IProgressMonitor)
	 */
	public void move(IModelElement container, IModelElement sibling, String rename, boolean force,
			IProgressMonitor monitor) throws PHPModelException {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#move() not yet implemented!");
	}

	/**
	 * @see ISourceManipulation#rename(String, boolean, IProgressMonitor)
	 */
	public void rename(String newName, boolean force, IProgressMonitor monitor) throws PHPModelException {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#rename() not yet implemented!");
	}

	/**
	 * Debugging purposes
	 */
	@Override
	protected void toStringChildren(int tab, StringBuffer buffer, Object info) {
		if (tab == 0) {
			super.toStringChildren(tab, buffer, info);
		}
	}

	/**
	 * Debugging purposes
	 */
	@Override
	protected void toStringInfo(int tab, StringBuffer buffer, Object info, boolean showResolvedInfo) {
		buffer.append(tabString(tab));
		if (this.names.length == 0) {
			buffer.append("<default>"); //$NON-NLS-1$
		}
		else {
			toStringName(buffer);
		}
		if (info == null) {
			buffer.append(" (not open)"); //$NON-NLS-1$
		}
		else {
			if (tab > 0) {
				buffer.append(" (...)"); //$NON-NLS-1$
			}
		}
	}

	/**
	 * @see IPHPElement#getAttachedPHPdoc(IProgressMonitor)
	 */
	public String getAttachedPHPdoc(IProgressMonitor monitor) throws PHPModelException {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#getAttachedPHPdoc() not yet implemented!");
	}

	protected IStatus validateExistence(IResource underlyingResource) {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#validateExistence() not yet implemented!");
	}

	@Override
	public void printNode(CorePrinter arg0) {
	// TODO Auto-generated method stub

	}

	/**
	 *
	 */
	public IPHPModel getPHPModel() {
		final IPHPModel phpModel = (IPHPModel) getModel();
		return phpModel;
	}

	/**
	 *
	 */
	public IPHPProject getPHPProject() {
		final IPHPProject phpProject = (IPHPProject) getScriptProject();
		return phpProject;
	}

	public ISchedulingRule getSchedulingRule() {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#getScriptFile() not yet implemented!");
	}

	public IResource getResource() {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#getScriptFile() not yet implemented!");
	}
}
